package com.caocaog.market.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.caocaog.market.entity.*;
import com.caocaog.market.entity.dto.GoodsAttrDTO;
import com.caocaog.market.entity.dto.GoodsDTO;
import com.caocaog.market.entity.dto.GoodsSpecDTO;
import com.caocaog.market.entity.vo.AllGoodsVO;
import com.caocaog.market.entity.vo.GoodsVO;
import com.caocaog.market.service.*;
import com.caocaog.market.util.CollectionsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.StoreConstant;
import org.jeecg.common.util.RedisUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static org.jeecg.common.system.base.service.impl.GenericServiceImpl.CACHE_TIME_DEFAULT;

/**
 * 商品信息
 *
 * @author chechen
 * @date 2022-04-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GoodsServiceImpl implements GoodsService {

    private final RedisUtil redisUtil;
    private final GoodsHisService goodsHisService;
    private final GoodsBaseService goodsBaseService;
    private final GoodsAttrService goodsAttrService;
    private final GoodsSpecService goodsSpecService;
    private final IShopBasicService shopBasicService;
    private final GoodsCategoriesService goodsCategoriesService;
    private final IGoodsChannelShipService goodsChannelShipService;


    @Override
    public List<AllGoodsVO> getAllGoodsInfo(String shopId) {
        log.info("获取状态为显示的商品分类 by 店铺id-> " + shopId);
        List<AllGoodsVO> categoriesInfoList = this.goodsCategoriesService.getShowGoodsCategoriesByShopId(shopId).stream()
                .map(AllGoodsVO::new).collect(Collectors.toList());
        // 循环获取并设置商品集合
        categoriesInfoList.forEach(categoriesInfo ->
                categoriesInfo.setGoodsList(this.getGoodsInfoByCategoriesId(categoriesInfo.getId())));

        return categoriesInfoList;
    }

    /**
     * 热门推荐商品
     *
     * @param shopId 店铺id
     * @return 热门的商品展示List
     */
    @Override
    public List<GoodsVO> getHotGoodsInfoByShopId(String shopId) {
        List<GoodsVO> goodsVOList = getAllGoodsInfoByShopId(shopId);
        if (CollectionsUtil.isEmpty(goodsVOList)) {
            return null;
        }
        return goodsVOList.stream().filter(GoodsVO::getHot).collect(Collectors.toList());
    }

    /**
     * 首页展示的商品
     *
     * @param shopId 店铺id
     * @return 热门的商品展示List
     */
    @Override
    public List<GoodsVO> getIndexGoodsInfoByShopId(String shopId) {
        List<GoodsVO> goodsVOList = getAllGoodsInfoByShopId(shopId);
        if (CollectionsUtil.isEmpty(goodsVOList)) {
            return null;
        }
        return goodsVOList.stream().filter(e -> StoreConstant.IS_INDEX.equals(e.getIsIndex())).collect(Collectors.toList());
    }

    /**
     * 根据店铺Id获取到商品List
     *
     * @param shopId 商品Id
     * @return 查询到的商品List
     */
    @Override
    public List<GoodsVO> getAllGoodsInfoByShopId(String shopId) {
        List<GoodsVO> goodsVOList = new ArrayList<>();
        // 查类型
        List<GoodsCategories> categories = this.goodsCategoriesService.getShowGoodsCategoriesByShopId(shopId);
        // 根据类型查商品
        categories.forEach(e -> {
            List<GoodsVO> goodsInfoByCategoriesId = this.getGoodsInfoByCategoriesId(e.getId());
            if (!CollectionsUtil.isEmpty(goodsInfoByCategoriesId))
                goodsVOList.addAll(goodsInfoByCategoriesId);
        });
        return goodsVOList;
    }

    @Override
    public List<AllGoodsVO> getFirstCategoriesGoodsInfo(@NotNull String shopId) {
        int count = 0;
        final int LEAST_GOODS_COUNT = 20;
        log.info("获取状态为显示的商品分类 by 店铺id-> " + shopId);
        List<AllGoodsVO> categoriesInfoList = this.goodsCategoriesService.getShowGoodsCategoriesByShopId(shopId).stream()
                .map(goodsCategories -> new AllGoodsVO(goodsCategories).setGoodsList(Collections.emptyList()))
                .collect(Collectors.toList());

        for (AllGoodsVO allGoodsVO : categoriesInfoList) {
            allGoodsVO.setGoodsList(this.getGoodsInfoByCategoriesId(allGoodsVO.getId()));
            count += allGoodsVO.getGoodsList().size();
            if (count > LEAST_GOODS_COUNT) break;
        }

        return categoriesInfoList;
    }


    /**
     * 根据商品类型来查询货物信息
     *
     * @param categoriesId 商品分类id
     * @return 查询到的货物信息
     */
    @Override
    public synchronized List<GoodsVO> getGoodsInfoByCategoriesId(String categoriesId) {
        // 检测 Redis 中是否有缓存
        final String key = CacheConstant.GOODS_LIST_BY_CATEGORIES_ID + CommonConstant.DOUBLE_COLON + categoriesId;
        if (this.redisUtil.hasKey(key))
            return this.redisUtil.lGet(key, 0, -1, GoodsVO.class);

        log.info("获取未下架的商品基础信息 by 商品分类id-> " + categoriesId);
        List<GoodsVO> goodsInfoList = this.goodsBaseService.getShowGoodsBaseByCategoriesId(categoriesId).stream()
                .map(GoodsVO::new).collect(Collectors.toList());

        goodsInfoList.forEach(goodsInfo -> {
            // 获取商品属性 by 商品id
            goodsInfo.setGoodsAttributeList(this.goodsAttrService.getGoodsAttrByGoodsId(goodsInfo.getId()));
            // 获取商品规格 by 商品id
            goodsInfo.setGoodsSpecificationsList(this.goodsSpecService.getGoodsSpecByGoodsId(goodsInfo.getId()));
        });
        if (!goodsInfoList.isEmpty())
            this.redisUtil.lSet(key, CACHE_TIME_DEFAULT, goodsInfoList.toArray());

        return goodsInfoList;
    }


    @Override
    @Transactional
    public GoodsVO addGoods(GoodsDTO goods) {
        List<GoodsAttrDTO> goodsAttrDTOs = goods.getGoodsAttributeVoList();
        List<GoodsSpecDTO> goodsSpecDTOs = goods.getGoodsSpecificationsVoList();

        log.info("插入商品基础信息");
        GoodsBase goodsBase = this.goodsBaseService.insertGoodsBase(GoodsBaseServiceImpl.dataProcess(goods, goodsSpecDTOs));
        final String goodsId = goodsBase.getId();

        log.info("批插入商品属性");
        List<GoodsAttr> goodsAttrs = goodsAttrDTOs.stream().map(goodsAttrDTO ->
                this.goodsAttrService.insertGoodsAttr(goodsAttrDTO.toGoodsAttr().setGoodsId(goodsId))
        ).collect(Collectors.toList());

        log.info("批插入商品规格");
        List<GoodsSpec> goodsSpecs = goodsSpecDTOs.stream().map(goodsSpecDTO ->
                this.goodsSpecService.insertGoodsSpec(goodsSpecDTO.toGoodsSpec().setGoodsId(goodsId)
                        /* TODO 等前端把小程序选择规格界面的图片不再是规格的Picture时就把这删了 */
                        .setPicture(goodsBase.getPicture())
                )).collect(Collectors.toList());

        log.info("插入商品历史");
        this.goodsHisService.insertGoodsHis(goodsBase, goodsAttrs, goodsSpecs);

        // 检测商品数量是否超出限定数量
        this.checkManyGoods(goodsBase.getShopId());
        // 删除 GOODS_LIST_BY_CATEGORIES_ID 缓存
        this.redisUtil.del(GoodsBaseServiceImpl.getGoodsListByCategoriesIdKey(goodsBase));

        return new GoodsVO(goodsBase)
                .setGoodsAttributeList(goodsAttrs)
                .setGoodsSpecificationsList(goodsSpecs)
                .preprocess();
    }

    @Override
    @Transactional
    public GoodsVO editGoods(GoodsDTO goods) {
        List<GoodsAttrDTO> goodsAttrDTOs = goods.getGoodsAttributeVoList();
        List<GoodsSpecDTO> goodsSpecDTOs = goods.getGoodsSpecificationsVoList();

        log.info("更新商品基础信息");
        GoodsBase goodsBase = this.goodsBaseService.updateGoodsBase(GoodsBaseServiceImpl.dataProcess(goods, goodsSpecDTOs));
        final String goodsId = goodsBase.getId();


        // 从 goodsAttrDTOs 分离出新增和修改的
        Map<Boolean, List<GoodsAttr>> goodsAttrInsertUpdateMap = goodsAttrDTOs.stream()
                .map(goodsAttrDTO -> goodsAttrDTO.toGoodsAttr().setGoodsId(goodsId))
                .collect(Collectors.groupingBy(goodsAttrDTO -> Objects.isNull(goodsAttrDTO.getId()), Collectors.toList()));
        List<GoodsAttr> pendingUpdateGoodsAttrList = goodsAttrInsertUpdateMap.getOrDefault(Boolean.FALSE, Collections.emptyList());
        List<GoodsAttr> pendingInsertGoodsAttrList = goodsAttrInsertUpdateMap.getOrDefault(Boolean.TRUE, Collections.emptyList());

        log.info("根据goodsId查出所有商品属性id集合");
        List<String> pendingDeleteGoodsAttrIds = this.goodsAttrService.getGoodsAttrByGoodsId(goodsId).stream().map(GoodsAttr::getId).collect(Collectors.toList());
        // 删除所以要更新的ids，剩下的就是被遗弃的
        pendingDeleteGoodsAttrIds.removeAll(pendingUpdateGoodsAttrList.stream().map(GoodsAttr::getId).collect(Collectors.toList()));


        if (!pendingUpdateGoodsAttrList.isEmpty()) {
            log.info("批量更新商品属性");
            this.goodsAttrService.updateBatchGoodsAttr(pendingUpdateGoodsAttrList);
        }
        if (!pendingInsertGoodsAttrList.isEmpty()) {
            log.info("批量插入商品属性");
            this.goodsAttrService.insertBatchGoodsAttr(pendingInsertGoodsAttrList);
        }
        if (!pendingDeleteGoodsAttrIds.isEmpty()) {
            log.info("删除商品属性");
            this.goodsAttrService.deleteByIdsCache(pendingDeleteGoodsAttrIds.toArray(new String[0]));
        }
        this.redisUtil.del(CacheConstant.GOODS_ATTR_LIST_BY_GOODS_ID + CommonConstant.DOUBLE_COLON + goods.getId());


        // 从 goodsSpecDTOs 分离出新增和修改的
        Map<Boolean, List<GoodsSpec>> goodsSpecInsertUpdateMap = goodsSpecDTOs.stream()
                .map(goodsSpecDTO -> goodsSpecDTO.toGoodsSpec().setGoodsId(goodsId)
                        /* TODO 等前端把小程序选择规格界面的图片不再是规格的Picture时就把这删了 */
                        .setPicture(goodsBase.getPicture())
                )
                .collect(Collectors.groupingBy(goodsSpecDTO -> Objects.isNull(goodsSpecDTO.getId()), Collectors.toList()));
        List<GoodsSpec> pendingUpdateGoodsSpecList = goodsSpecInsertUpdateMap.getOrDefault(Boolean.FALSE, Collections.emptyList());
        List<GoodsSpec> pendingInsertGoodsSpecList = goodsSpecInsertUpdateMap.getOrDefault(Boolean.TRUE, Collections.emptyList());

        log.info("根据goodsId查出所有商品规格id集合");
        List<String> pendingDeleteGoodsSpecIds = this.goodsSpecService.getGoodsSpecByGoodsId(goodsId).stream().map(GoodsSpec::getId).collect(Collectors.toList());
        // 删除所有要更新的ids，剩下的就是被遗弃的
        pendingDeleteGoodsSpecIds.removeAll(pendingUpdateGoodsSpecList.stream().map(GoodsSpec::getId).collect(Collectors.toList()));


        if (!pendingUpdateGoodsSpecList.isEmpty()) {
            log.info("批量更新商品规格");
            this.goodsSpecService.updateBatchGoodsSpec(pendingUpdateGoodsSpecList);
        }
        if (!pendingInsertGoodsSpecList.isEmpty()) {
            log.info("批量插入商品规格");
            this.goodsSpecService.insertBatchGoodsSpec(pendingInsertGoodsSpecList);
        }
        if (!pendingDeleteGoodsSpecIds.isEmpty()) {
            log.info("删除商品规格");
            this.goodsSpecService.deleteByIdsCache(pendingDeleteGoodsSpecIds.toArray(new String[0]));
        }
        this.redisUtil.del(CacheConstant.GOODS_SPEC_LIST_BY_GOODS_ID + CommonConstant.DOUBLE_COLON + goods.getId());


        log.info("根据goodsId查询商品属性");
        List<GoodsAttr> goodsAttrs = this.goodsAttrService.getGoodsAttrByGoodsId(goodsId);
        log.info("根据goodsId查询商品规格");
        List<GoodsSpec> goodsSpecs = this.goodsSpecService.getGoodsSpecByGoodsId(goodsId);
        log.info("插入商品历史");
        this.goodsHisService.insertGoodsHis(goodsBase, goodsAttrs, goodsSpecs);
        log.info("删除店铺所有购物车缓存");
        this.goodsBaseService.cleanShopCart(goodsBase.getShopId());

        // 删除 GOODS_LIST_BY_CATEGORIES_ID 缓存
        this.redisUtil.del(GoodsBaseServiceImpl.getGoodsListByCategoriesIdKey(goodsBase));

        return new GoodsVO(goodsBase)
                .setGoodsAttributeList(goodsAttrs)
                .setGoodsSpecificationsList(goodsSpecs)
                .preprocess();
    }

    /**
     * 根据频道ID查询商品信息，走缓存
     *
     * @param channelId 频道ID
     * @return 查询结果
     */
    @Override
    public List<GoodsBase> getAllGoodsByChannel(String channelId) {
        return redisUtil.executeHGetWithLock(CacheConstant.GOODS_CHANNEL_MAP, channelId, () -> goodsBaseService.getAllGoodsByChannelId(channelId));
    }




    @Override
    @Transactional
    public int deleteGoods(String... ids) {
        log.info("先查询出所有的商品基础信息");
        List<GoodsBase> goodsBaseList = this.goodsBaseService.selectListByIdsCache(ids);
        log.info("再删除商品基础信息");
        int deleteRecordCount = this.goodsBaseService.deleteByEntityListAllCache(goodsBaseList);

        // 检测商品数量是否没有超出限定数量
        if (deleteRecordCount > 0) {
            this.checkNotManyGoods(goodsBaseList.get(0).getShopId());
        }

        // 删除 GOODS_LIST_BY_CATEGORIES_ID 缓存
        this.redisUtil.del(goodsBaseList.stream()
                .map(goodsBase -> Arrays.asList(GoodsBaseServiceImpl.getGoodsListByCategoriesIdKey(goodsBase)))
                .flatMap(Collection::stream)
                .toArray(String[]::new));
        // 删除商品频道的缓存
        List<String> goodIds = goodsBaseList.stream().map(GoodsBase::getId).collect(Collectors.toList());
        // 根据goodIds查询出来所属频道ID
        List<String> channelIds = goodsChannelShipService.list(new LambdaQueryWrapper<GoodsChannelShip>().in(GoodsChannelShip::getGoodsId, goodIds)).stream().map(GoodsChannelShip::getChannelId).collect(Collectors.toList());
        // 若集合中有值就删除这个key,以及 channel-good ship表中的内容
        if (!CollectionsUtil.isEmpty(channelIds)){
            channelIds.stream().map(channelId->CacheConstant.build(CacheConstant.GOODS_CHANNEL_MAP,channelId)).forEach(redisUtil::del);
            goodsChannelShipService.remove(new LambdaQueryWrapper<GoodsChannelShip>().in(GoodsChannelShip::getGoodsId,goodIds));
        }
        log.info("循环删除商品属性与商品规格");
        for (String id : ids) {
            this.goodsAttrService.deleteGoodsAttrByGoodsId(id);
            this.goodsSpecService.deleteGoodsSpecByGoodsId(id);
        }

        /*删除了就不插入历史了，因为历史的作用就是怕原数据总是更改，不好回溯，既然数据已经删除了，
        就表明这条数据永远不会再更改了，况且再插入历史会非常耗时，综上所述，故删除时不再插入商品历史
        goodsHisService.insertGoodsHis(goodsBase, true, goodsAttrs, goodsSpecs);*/
        return deleteRecordCount;
    }

    public static void main(String[] args) {

    }

    /**
     * 检测商品数量是否超出限定数量
     *
     * @param shopId 商品id
     */
    private void checkManyGoods(String shopId) {
        String[] goodsCategoriesIds = this.goodsCategoriesService.getGoodsCategoriesIdsByShopIds(shopId);
        String[] goodsIds = this.goodsBaseService.getGoodsIdsByCategoriesIds(goodsCategoriesIds);
        if (goodsIds.length > CommonConstant.GOODS_COUNT_MAX) {
            this.shopBasicService.lambdaUpdate().set(ShopBasic::getManyGoods, Boolean.TRUE);
        }
    }


    /**
     * 检测商品数量是否没有超出限定数量
     *
     * @param shopId 商品id
     */
    private void checkNotManyGoods(String shopId) {
        String[] goodsCategoriesIds = this.goodsCategoriesService.getGoodsCategoriesIdsByShopIds(shopId);
        String[] goodsIds = this.goodsBaseService.getGoodsIdsByCategoriesIds(goodsCategoriesIds);
        if (goodsIds.length < CommonConstant.GOODS_COUNT_MAX) {
            this.shopBasicService.lambdaUpdate().set(ShopBasic::getManyGoods, Boolean.FALSE);
        }
    }

}
